# -------------------------------------01 加载波士顿房价数据-----------------------
from keras.datasets import boston_housing

(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
# print(train_data.shape) #(404, 13)
# print(test_data.shape) #(102, 13)
# 如你所见，我们有 404 个训练样本和 102 个测试样本，每个样本都有 13 个数值特征，比如
# 人均犯罪率、每个住宅的平均房间数、高速公路可达性等。
# 目标是房屋价格的中位数，单位是千美元。

# print(train_targets)
# 目标是房屋价格的中位数，单位是千美元。
# [ 15.2  42.3  50.   21.1  17.7  18.5  11.3  15.6  15.6  14.4  12.1  17.9
#   23.1  19.9  15.7   8.8  50.   22.5  24.1  27.5  10.9  30.8  32.9  24.
#   18.5  13.3  22.9  34.7  16.6  17.5  22.3  16.1  14.9  23.1  34.9  25.
#   13.9  13.1  20.4  20.   15.2  24.7  22.2  16.7  12.7  15.6  18.4  21.
#   30.1  15.1  18.7   9.6  31.5  24.8  19.1  22.   14.5  11.   32.   29.4
#   20.3  24.4  14.6  19.5  14.1  14.3  15.6  10.5   6.3  19.3  19.3  13.4
#   36.4  17.8  13.5  16.5   8.3  14.3  16.   13.4  28.6  43.5  20.2  22.
#   23.   20.7  12.5  48.5  14.6  13.4  23.7  50.   21.7  39.8  38.7  22.2
#   34.9  22.5  31.1  28.7  46.   41.7  21.   26.6  15.   24.4  13.3  21.2
#   11.7  21.7  19.4  50.   22.8  19.7  24.7  36.2  14.2  18.9  18.3  20.6
#   24.6  18.2   8.7  44.   10.4  13.2  21.2  37.   30.7  22.9  20.   19.3
#   31.7  32.   23.1  18.8  10.9  50.   19.6   5.   14.4  19.8  13.8  19.6
#   23.9  24.5  25.   19.9  17.2  24.6  13.5  26.6  21.4  11.9  22.6  19.6
#    8.5  23.7  23.1  22.4  20.5  23.6  18.4  35.2  23.1  27.9  20.6  23.7
#   28.   13.6  27.1  23.6  20.6  18.2  21.7  17.1   8.4  25.3  13.8  22.2
#   18.4  20.7  31.6  30.5  20.3   8.8  19.2  19.4  23.1  23.   14.8  48.8
#   22.6  33.4  21.1  13.6  32.2  13.1  23.4  18.9  23.9  11.8  23.3  22.8
#   19.6  16.7  13.4  22.2  20.4  21.8  26.4  14.9  24.1  23.8  12.3  29.1
#   21.   19.5  23.3  23.8  17.8  11.5  21.7  19.9  25.   33.4  28.5  21.4
#   24.3  27.5  33.1  16.2  23.3  48.3  22.9  22.8  13.1  12.7  22.6  15.
#   15.3  10.5  24.   18.5  21.7  19.5  33.2  23.2   5.   19.1  12.7  22.3
#   10.2  13.9  16.3  17.   20.1  29.9  17.2  37.3  45.4  17.8  23.2  29.
#   22.   18.   17.4  34.6  20.1  25.   15.6  24.8  28.2  21.2  21.4  23.8
#   31.   26.2  17.4  37.9  17.5  20.    8.3  23.9   8.4  13.8   7.2  11.7
#   17.1  21.6  50.   16.1  20.4  20.6  21.4  20.6  36.5   8.5  24.8  10.8
#   21.9  17.3  18.9  36.2  14.9  18.2  33.3  21.8  19.7  31.6  24.8  19.4
#   22.8   7.5  44.8  16.8  18.7  50.   50.   19.5  20.1  50.   17.2  20.8
#   19.3  41.3  20.4  20.5  13.8  16.5  23.9  20.6  31.5  23.3  16.8  14.
#   33.8  36.1  12.8  18.3  18.7  19.1  29.   30.1  50.   50.   22.   11.9
#   37.6  50.   22.7  20.8  23.5  27.9  50.   19.3  23.9  22.6  15.2  21.7
#   19.2  43.8  20.3  33.2  19.9  22.5  32.7  22.   17.1  19.   15.   16.1
#   25.1  23.7  28.7  37.2  22.6  16.4  25.   29.8  22.1  17.4  18.1  30.3
#   17.5  24.7  12.6  26.5  28.7  13.3  10.4  24.4  23.   20.   17.8   7.
#   11.8  24.4  13.8  19.4  25.2  19.4  19.4  29.1]


# -------------------------------------02 数据标准化-----------------------
# 将取值范围差异很大的数据输入到神经网络中，这是有问题的。网络可能会自动适应这种
# 取值范围不同的数据，但学习肯定变得更加困难。对于这种数据，普遍采用的最佳实践是对每
# 个特征做标准化，即对于输入数据的每个特征（输入数据矩阵中的列），减去特征平均值，再除
# 以标准差，这样得到的特征平均值为 0，标准差为 1。用 Numpy 可以很容易实现标准化。
mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std

test_data -= mean
test_data /= std
# 注意，用于测试数据标准化的均值和标准差都是在训练数据上计算得到的。在工作流程中，
# 你不能使用在测试数据上计算得到的任何结果，即使是像数据标准化这么简单的事情也不行。

# -------------------------------------03 模型定义-----------------------
from keras import models
from keras import layers


# 因为需要将同一个模型多次实例化，
# 所以用一个函数来构建模型
def build_model():
    model = models.Sequential()
    model.add(layers.Dense(64, activation='relu',
                           input_shape=(train_data.shape[1],)))
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(1))
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    return model


# 网络的最后一层只有一个单元，没有激活，是一个线性层。这是标量回归（标量回归是预
# 测单一连续值的回归）的典型设置。添加激活函数将会限制输出范围。例如，如果向最后一层
# 添加 sigmoid 激活函数，网络只能学会预测 0~1 范围内的值。这里最后一层是纯线性的，所以
# 网络可以学会预测任意范围内的值。
# 注意，编译网络用的是 mse 损失函数，即均方误差（MSE， mean squared error），预测值与
# 目标值之差的平方。这是回归问题常用的损失函数。
# 在训练过程中还监控一个新指标： 平均绝对误差（MAE， mean absolute error）。它是预测值
# 与目标值之差的绝对值。比如，如果这个问题的 MAE 等于 0.5，就表示你预测的房价与实际价
# 格平均相差 500 美元。

# -------------------------------------04 利用 K 折验证来验证你的方法-----------------------
# 为了在调节网络参数（比如训练的轮数）的同时对网络进行评估，你可以将数据划分为训
# 练集和验证集，正如前面例子中所做的那样。但由于数据点很少，验证集会非常小（比如大约
# 100 个样本）。因此，验证分数可能会有很大波动，这取决于你所选择的验证集和训练集。也就
# 是说，验证集的划分方式可能会造成验证分数上有很大的方差，这样就无法对模型进行可靠的
# 评估。
# 在这种情况下，最佳做法是使用 K 折交叉验证（见图 3-11）。这种方法将可用数据划分为 K
# 个分区（K 通常取 4 或 5），实例化 K 个相同的模型，将每个模型在 K-1 个分区上训练，并在剩
# 下的一个分区上进行评估。模型的验证分数等于 K 个验证分数的平均值。这种方法的代码实现
# 很简单。

# K 折验证
import numpy as np

k = 4
num_val_samples = len(train_data) // k
num_epochs = 100
all_scores = []
for i in range(k):
    print('processing fold #', i)
    val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
    val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]
    partial_train_data = np.concatenate(
        [train_data[:i * num_val_samples],
         train_data[(i + 1) * num_val_samples:]],
        axis=0)
    partial_train_targets = np.concatenate(
        [train_targets[:i * num_val_samples],
         train_targets[(i + 1) * num_val_samples:]],
        axis=0)
    model = build_model()
    model.fit(partial_train_data, partial_train_targets,
              epochs=num_epochs, batch_size=1, verbose=0)
    val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
    all_scores.append(val_mae)
print(all_scores)
